use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Write, BufWriter, Seek, SeekFrom};
use std::path::Path;
use serde::{Deserialize, Serialize};
use anyhow::{Result, Context};

// 分割配置结构体
#[derive(Debug, Serialize, Deserialize)]
pub struct SplitConfig {
    pub split_type: String, // "lines" 或 "size"
    pub lines_per_file: Option<u64>, // 按行数分割时使用
    pub size_per_file: Option<u64>, // 按大小分割时使用（字节）
    pub output_dir: String, // 输出目录
    pub file_prefix: String, // 输出文件前缀
}

// 分割进度信息
#[derive(Debug, Serialize, Deserialize)]
pub struct SplitProgress {
    pub current_file: u32,
    pub total_size: u64,
    pub processed_size: u64,
    pub percentage: f64,
    pub status: String,
}

// 选择文件对话框
#[tauri::command]
async fn select_file(app: tauri::AppHandle) -> Result<Option<String>, String> {
    use tauri_plugin_dialog::DialogExt;
    use tokio::sync::oneshot;
    
    let (tx, rx) = oneshot::channel();
    
    app.dialog()
        .file()
        .add_filter("所有文件", &["*"])
        .add_filter("文本文件", &["txt", "log", "csv"])
        .add_filter("SQL文件", &["sql"])
        .add_filter("JSON文件", &["json"])
        .pick_file(move |file_path| {
            let _ = tx.send(file_path);
        });
    
    let file_path = rx.await.map_err(|_| "文件选择被取消".to_string())?;
    Ok(file_path.map(|p| match p {
        tauri_plugin_dialog::FilePath::Path(path) => path.to_string_lossy().to_string(),
        tauri_plugin_dialog::FilePath::Url(url) => url.to_string(),
    }))
}

// 选择输出目录
#[tauri::command]
async fn select_output_dir(app: tauri::AppHandle) -> Result<Option<String>, String> {
    use tauri_plugin_dialog::DialogExt;
    use tokio::sync::oneshot;
    
    let (tx, rx) = oneshot::channel();
    
    app.dialog()
        .file()
        .pick_folder(move |dir_path| {
            let _ = tx.send(dir_path);
        });
    
    let dir_path = rx.await.map_err(|_| "目录选择被取消".to_string())?;
    Ok(dir_path.map(|p| match p {
        tauri_plugin_dialog::FilePath::Path(path) => path.to_string_lossy().to_string(),
        tauri_plugin_dialog::FilePath::Url(url) => url.to_string(),
    }))
}

// 获取文件信息
#[tauri::command]
async fn get_file_info(file_path: String) -> Result<serde_json::Value, String> {
    let path = Path::new(&file_path);
    
    if !path.exists() {
        return Err("文件不存在".to_string());
    }
    
    let metadata = std::fs::metadata(path)
        .map_err(|e| format!("无法读取文件信息: {}", e))?;
    
    let file_size = metadata.len();
    
    // 计算行数（对于文本文件）
    let line_count = count_lines(&file_path)
        .map_err(|e| format!("无法计算行数: {}", e))?;
    
    Ok(serde_json::json!({
        "size": file_size,
        "lines": line_count,
        "name": path.file_name().unwrap_or_default().to_string_lossy()
    }))
}

// 计算文件行数
fn count_lines(file_path: &str) -> Result<u64> {
    let file = File::open(file_path)
        .context("无法打开文件")?;
    let reader = BufReader::new(file);
    
    let mut count = 0u64;
    for _ in reader.lines() {
        count += 1;
    }
    
    Ok(count)
}

// 按行数分割文件
#[tauri::command]
async fn split_by_lines(
    file_path: String,
    lines_per_file: u64,
    output_dir: String,
    file_prefix: String,
) -> Result<String, String> {
    tokio::task::spawn_blocking(move || {
        split_by_lines_impl(&file_path, lines_per_file, &output_dir, &file_prefix)
    })
    .await
    .map_err(|e| format!("任务执行失败: {}", e))?
    .map_err(|e| format!("分割失败: {}", e))
}

fn split_by_lines_impl(
    file_path: &str,
    lines_per_file: u64,
    output_dir: &str,
    file_prefix: &str,
) -> Result<String> {
    let input_file = File::open(file_path)
        .context("无法打开输入文件")?;
    let reader = BufReader::new(input_file);
    
    let output_path = Path::new(output_dir);
    std::fs::create_dir_all(output_path)
        .context("无法创建输出目录")?;
    
    // 获取源文件扩展名
    let source_ext = Path::new(file_path)
        .extension()
        .and_then(|ext| ext.to_str())
        .unwrap_or("txt");
    
    let mut current_file_index = 1;
    let mut current_line_count = 0;
    let mut current_writer: Option<BufWriter<File>> = None;
    let mut total_files = 0;
    
    for line_result in reader.lines() {
        let line = line_result.context("读取行失败")?;
        
        // 如果需要创建新文件
        if current_line_count == 0 {
            let output_file_path = output_path.join(format!(
                "{}_{:04}.{}",
                file_prefix,
                current_file_index,
                source_ext
            ));
            
            let output_file = OpenOptions::new()
                .create(true)
                .write(true)
                .truncate(true)
                .open(&output_file_path)
                .context("无法创建输出文件")?;
            
            current_writer = Some(BufWriter::new(output_file));
            total_files += 1;
        }
        
        // 写入当前行
        if let Some(ref mut writer) = current_writer {
            writeln!(writer, "{}", line)
                .context("写入文件失败")?;
        }
        
        current_line_count += 1;
        
        // 如果达到每个文件的行数限制
        if current_line_count >= lines_per_file {
            if let Some(writer) = current_writer.take() {
                writer.into_inner()
                    .context("刷新文件缓冲区失败")?;
            }
            current_file_index += 1;
            current_line_count = 0;
        }
    }
    
    // 确保最后一个文件被正确关闭
    if let Some(writer) = current_writer {
        writer.into_inner()
            .context("刷新最后文件缓冲区失败")?;
    }
    
    Ok(format!("成功分割为 {} 个文件", total_files))
}

// 按大小分割文件
#[tauri::command]
async fn split_by_size(
    file_path: String,
    size_per_file: u64,
    output_dir: String,
    file_prefix: String,
) -> Result<String, String> {
    tokio::task::spawn_blocking(move || {
        split_by_size_impl(&file_path, size_per_file, &output_dir, &file_prefix)
    })
    .await
    .map_err(|e| format!("任务执行失败: {}", e))?
    .map_err(|e| format!("分割失败: {}", e))
}

// 按符号分割文件
#[tauri::command]
async fn split_by_symbol(
    file_path: String,
    separator: String,
    items_per_file: u64,
    output_dir: String,
    file_prefix: String,
) -> Result<String, String> {
    tokio::task::spawn_blocking(move || {
        split_by_symbol_impl(&file_path, &separator, items_per_file, &output_dir, &file_prefix)
    })
    .await
    .map_err(|e| format!("任务执行失败: {}", e))?
    .map_err(|e| format!("分割失败: {}", e))
}

fn split_by_size_impl(
    file_path: &str,
    size_per_file: u64,
    output_dir: &str,
    file_prefix: &str,
) -> Result<String> {
    let mut input_file = File::open(file_path)
        .context("无法打开输入文件")?;
    
    let output_path = Path::new(output_dir);
    std::fs::create_dir_all(output_path)
        .context("无法创建输出目录")?;
    
    let mut current_file_index = 1;
    let mut total_files = 0;
    let mut buffer = vec![0u8; 8192]; // 8KB 缓冲区
    
    loop {
        let output_file_path = output_path.join(format!(
            "{}_{:04}.part",
            file_prefix,
            current_file_index
        ));
        
        let output_file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&output_file_path)
            .context("无法创建输出文件")?;
        
        let mut writer = BufWriter::new(output_file);
        let mut current_file_size = 0u64;
        
        // 写入当前文件直到达到大小限制
        while current_file_size < size_per_file {
            let remaining_size = size_per_file - current_file_size;
            let read_size = std::cmp::min(buffer.len() as u64, remaining_size) as usize;
            
            let bytes_read = std::io::Read::read(&mut input_file, &mut buffer[..read_size])
                .context("读取文件失败")?;
            
            if bytes_read == 0 {
                // 文件结束
                break;
            }
            
            writer.write_all(&buffer[..bytes_read])
                .context("写入文件失败")?;
            
            current_file_size += bytes_read as u64;
        }
        
        writer.into_inner()
            .context("刷新文件缓冲区失败")?;
        
        total_files += 1;
        current_file_index += 1;
        
        // 检查是否还有更多数据
        let mut test_buffer = [0u8; 1];
        match std::io::Read::read(&mut input_file, &mut test_buffer) {
            Ok(0) => break, // 文件结束
            Ok(_) => {
                // 还有数据，回退一个字节
                input_file.seek(SeekFrom::Current(-1))
                    .context("文件定位失败")?;
            }
            Err(e) => return Err(anyhow::anyhow!("读取测试失败: {}", e)),
        }
    }
    
    Ok(format!("成功分割为 {} 个文件", total_files))
}

fn split_by_symbol_impl(
    file_path: &str,
    separator: &str,
    items_per_file: u64,
    output_dir: &str,
    file_prefix: &str,
) -> Result<String> {
    // 读取整个文件内容
    let content = std::fs::read_to_string(file_path)
        .context("无法读取输入文件")?;
    
    let output_path = Path::new(output_dir);
    std::fs::create_dir_all(output_path)
        .context("无法创建输出目录")?;
    
    // 获取源文件扩展名
    let source_ext = Path::new(file_path)
        .extension()
        .and_then(|ext| ext.to_str())
        .unwrap_or("txt");
    
    // 根据分隔符分割内容
    let parts: Vec<&str> = content.split(separator).collect();
    
    if parts.len() <= 1 {
        return Err(anyhow::anyhow!("未找到分隔符或文件内容为空"));
    }
    
    // 重新组合分割后的内容，保留分隔符
    let mut items = Vec::new();
    for (i, part) in parts.iter().enumerate() {
        if i == 0 {
            // 第一部分可能不包含分隔符开头
            if !part.trim().is_empty() {
                items.push(part.to_string());
            }
        } else {
            // 其他部分需要重新添加分隔符
            let item = format!("{}{}", separator, part);
            items.push(item);
        }
    }
    
    if items.is_empty() {
        return Err(anyhow::anyhow!("没有找到有效的内容块"));
    }
    
    // 按照配置的数量将项目分组到不同文件
    let mut current_file_index = 1;
    let mut total_files = 0;
    
    for chunk in items.chunks(items_per_file as usize) {
        let output_file_path = output_path.join(format!(
            "{}_{:04}.{}",
            file_prefix,
            current_file_index,
            source_ext
        ));
        
        let output_file = OpenOptions::new()
            .create(true)
            .write(true)
            .truncate(true)
            .open(&output_file_path)
            .context("无法创建输出文件")?;
        
        let mut writer = BufWriter::new(output_file);
        
        // 写入当前块的所有项目
        for item in chunk {
            writer.write_all(item.as_bytes())
                .context("写入文件失败")?;
        }
        
        writer.into_inner()
            .context("刷新文件缓冲区失败")?;
        
        total_files += 1;
        current_file_index += 1;
    }
    
    Ok(format!("成功分割为 {} 个文件", total_files))
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_opener::init())
        .plugin(tauri_plugin_dialog::init())
        .invoke_handler(tauri::generate_handler![
            select_file,
            select_output_dir,
            get_file_info,
            split_by_lines,
            split_by_size,
            split_by_symbol
        ])
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
